package com.yfux.cling_player.service;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;

import com.yfux.cling_player.nums.RenderState;
import com.yfux.cling_player.service.controller.AVTransportController;
import com.yfux.cling_player.service.controller.AudioControl;
import com.yfux.cling_player.service.controller.AudioRenderController;
import com.yfux.cling_player.service.controller.AvTransportControl;
import com.yfux.cling_player.service.controller.RenderControl;
import com.yfux.cling_player.util.LogUtil;
import com.yfux.cling_player.util.WifiUtil;

import org.fourthline.cling.UpnpServiceConfiguration;
import org.fourthline.cling.android.AndroidUpnpServiceConfiguration;
import org.fourthline.cling.android.AndroidUpnpServiceImpl;
import org.fourthline.cling.binding.annotations.AnnotationLocalServiceBinder;
import org.fourthline.cling.model.ServiceManager;
import org.fourthline.cling.model.ValidationException;
import org.fourthline.cling.model.meta.DeviceDetails;
import org.fourthline.cling.model.meta.DeviceIdentity;
import org.fourthline.cling.model.meta.Icon;
import org.fourthline.cling.model.meta.LocalDevice;
import org.fourthline.cling.model.meta.LocalService;
import org.fourthline.cling.model.meta.ManufacturerDetails;
import org.fourthline.cling.model.meta.ModelDetails;
import org.fourthline.cling.model.types.UDADeviceType;
import org.fourthline.cling.model.types.UDAServiceId;
import org.fourthline.cling.model.types.UDN;
import org.fourthline.cling.support.avtransport.lastchange.AVTransportLastChangeParser;
import org.fourthline.cling.support.avtransport.lastchange.AVTransportVariable;
import org.fourthline.cling.support.lastchange.EventedValue;
import org.fourthline.cling.support.lastchange.LastChangeAwareServiceManager;
import org.fourthline.cling.support.model.Channel;
import org.fourthline.cling.support.renderingcontrol.lastchange.ChannelVolume;
import org.fourthline.cling.support.renderingcontrol.lastchange.RenderingControlLastChangeParser;
import org.fourthline.cling.support.renderingcontrol.lastchange.RenderingControlVariable;

import java.io.IOException;
import java.util.UUID;

public class DLNARendererService extends AndroidUpnpServiceImpl {
    private static final int START_STICKY = 0;
    private final LogUtil logger = new LogUtil("DLNARendererService");
    private RendererServiceBinderWrapper serviceBinder;
    private AvTransportControl avTransportControl;
    private AudioControl audioControl;
    private LocalDevice localDevice;

    public static void startService(Context context) {
        context.getApplicationContext().startService(
                new Intent(context, DLNARendererService.class));
    }

    @Override
    protected UpnpServiceConfiguration createConfiguration() {
        return new AndroidUpnpServiceConfiguration() {
            @Override
            public int getAliveIntervalMillis() {
                return 5 * 1000;
            }
        };
    }

    @Override
    public void onCreate() {
        logger.i("DLNARendererService create.");
        super.onCreate();
        avTransportControl = new AVTransportController(getApplicationContext());
        audioControl = new AudioRenderController(getApplicationContext());
        try {
            localDevice = createRendererDevice(WifiUtil.getHttpBaseUrl(getApplicationContext(),
                    9091));
            upnpService.getRegistry().addDevice(localDevice);
        } catch (Exception e) {
            e.printStackTrace();
            stopSelf();
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return Service.START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        if (serviceBinder == null) {
            serviceBinder = new RendererServiceBinderWrapper();
        }
        return serviceBinder;
    }

    @Override
    public void onDestroy() {
        logger.w("DLNARendererService destroy.");
        if (localDevice != null) {
            upnpService.getRegistry().removeDevice(localDevice);
        }
        if (avTransportControl instanceof AVTransportController) {
            ((AVTransportController) avTransportControl).setMediaControl(null);
        }
        super.onDestroy();
    }

    protected LocalDevice createRendererDevice(String baseUrl) throws ValidationException, IOException {
        String info = "DLNA_MediaPlayer-" + baseUrl + "-" + Build.MODEL + "-" + Build.MANUFACTURER;
        UDN udn;
        try {
            udn = new UDN(UUID.nameUUIDFromBytes(info.getBytes()));
        } catch (Exception ex) {
            udn = new UDN(UUID.randomUUID());
        }
        logger.i("create local device: [MediaRenderer][" + udn.getIdentifierString().split("-")[udn.getIdentifierString().split("-").length - 1] + "](" + baseUrl + ")");
        return new LocalDevice(
                new DeviceIdentity(udn),
                new UDADeviceType("MediaRenderer", 1),
                new DeviceDetails(
                        "不想投屏(o_O)",
                        new ManufacturerDetails(Build.MANUFACTURER),
                        new ModelDetails(Build.MODEL,
                                "MPI MediaPlayer",
                                "v1",
                                baseUrl)),
                new Icon[]{},
                generateLocalServices()
        );
    }

    @SuppressWarnings("unchecked")
    protected LocalService<?>[] generateLocalServices() {
        AnnotationLocalServiceBinder serviceBinder = new AnnotationLocalServiceBinder();
        // av transport service
        LocalService<AVTransportServiceImpl> avTransportService = serviceBinder.read(AVTransportServiceImpl.class);
        avTransportService.setManager(new LastChangeAwareServiceManager<AVTransportServiceImpl>(
                avTransportService, new AVTransportLastChangeParser()) {
            @Override
            protected AVTransportServiceImpl createServiceInstance() {
                return new AVTransportServiceImpl(avTransportControl);
            }
        });
        // render service
        LocalService<AudioRenderServiceImpl> renderingControlService = serviceBinder.read(
                AudioRenderServiceImpl.class);
        renderingControlService.setManager(new LastChangeAwareServiceManager<
                AudioRenderServiceImpl>(renderingControlService, new RenderingControlLastChangeParser()) {
            @Override
            protected AudioRenderServiceImpl createServiceInstance() {
                return new AudioRenderServiceImpl(audioControl);
            }
        });
        return new LocalService[]{avTransportService, renderingControlService};
    }

    public void bindRealPlayer(RenderControl control) {
        if (avTransportControl instanceof AVTransportController) {
            ((AVTransportController) avTransportControl).setMediaControl(control);
        }
    }

    public void notifyAvTransportLastChange(RenderState state) {
        notifyAvTransportLastChange(new AVTransportVariable.TransportState(state.toTransportState()));
    }

    private void notifyAvTransportLastChange(EventedValue<?> event) {
        if (localDevice != null) {
            LocalService service = localDevice.findService(new UDAServiceId("AVTransport"));
            if (service != null) {
                ServiceManager manager = service.getManager();
                if (manager != null && manager.getImplementation()
                        instanceof AVTransportServiceImpl) {
                    ((AVTransportServiceImpl) manager.getImplementation()).getLastChange()
                            .setEventedValue(0, event);
                }
                if (manager != null && manager instanceof LastChangeAwareServiceManager) {
                    ((LastChangeAwareServiceManager) manager).fireLastChange();
                }
            }
        }
    }

    public void notifyRenderControlLastChange(int volume) {
        if (localDevice != null) {
            LocalService service = localDevice.findService(new UDAServiceId("RenderingControl"));
            if (service != null) {
                ServiceManager manager = service.getManager();
                if (manager != null && manager.getImplementation() instanceof AudioRenderServiceImpl) {
                    ((AudioRenderServiceImpl) manager.getImplementation()).getLastChange().setEventedValue(
                            0, new RenderingControlVariable.Volume(
                                    new ChannelVolume(Channel.Master, volume)));
                }
                if (manager != null && manager instanceof LastChangeAwareServiceManager) {
                    ((LastChangeAwareServiceManager) manager).fireLastChange();
                }
            }
        }
    }

    public interface RendererServiceBinder {
        DLNARendererService getService();
    }

    protected class RendererServiceBinderWrapper extends Binder implements RendererServiceBinder {
        public DLNARendererService getService() {
            return DLNARendererService.this;
        }
    }
}